#ifndef SDFS_PROTOBUF_CODEC_H
#define SDFS_PROTOBUF_CODEC_H

#include <muduo/base/Logging.h>
#include <muduo/net/Buffer.h>
#include <muduo/net/TcpConnection.h>

#include <google/protobuf/message.h>

#include <boost/noncopyable.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>


/* message structure */

// struct ProtobufTransportFormat __attribute__ ((__packed__))
// {
//   int32_t  len;
//   int32_t  nameLen;
//   char     typeName[nameLen];
//   char     protobufData[len-nameLen-8];
//   int32_t  checkSum; // adler32 of nameLen, typeName and protobufData
// }

using namespace boost;

namespace sdfs
{
namespace protobuf
{

typedef boost::shared_ptr<google::protobuf::Message> MessagePtr;

class ProtobufCodec : boost::noncopyable
{
public:

	enum ErrorCode
	{
		kNoError = 0,
		kInvalidLength,
		kCheckSumError,
		kInvalidNameLen,
		kUnknownMessageType,
		kParseError,
	};

	typedef boost::function<void (const muduo::net::TcpConnectionPtr&,
					const MessagePtr&,
					muduo::Timestamp)> ProtobufMessageCallback;

	typedef boost::function<void (const muduo::net::TcpConnectionPtr&,
					muduo::net::Buffer*,
					muduo::Timestamp,
					ErrorCode)> ErrorCallback;

	explicit ProtobufCodec(const ProtobufMessageCallback& msgcb):
			messageCallback_(msgcb),
			errorCallback_(defaultErrorCallback)
	{}


	ProtobufCodec(const ProtobufMessageCallback& msgcb,
			const ErrorCallback& errorcb):
			messageCallback_(msgcb),
			errorCallback_(errorcb)
	{}

	void onMessage(const muduo::net::TcpConnectionPtr& conn,
			muduo::net::Buffer* buf,
			muduo::Timestamp recvTime);

	void Send(const muduo::net::TcpConnectionPtr& conn, 
			const google::protobuf::Message& message);

	static const muduo::string& errorCodeToString(ErrorCode code);

	static void fillEmptyBuffer(muduo::net::Buffer* buf, 
			const google::protobuf::Message& message);

	static google::protobuf::Message* createMessage(const std::string& 	type_name);

	static MessagePtr parse(const char* buf, int len, ErrorCode* code);

private:
	static void defaultErrorCallback(const muduo::net::TcpConnectionPtr& conn,
				muduo::net::Buffer* buf,
				muduo::Timestamp,
				ErrorCode code);

	ProtobufMessageCallback messageCallback_;
	ErrorCallback errorCallback_;

	const static int kHeaderLen = sizeof(int32_t);
	const static int kMinMessageLen = 2*kHeaderLen + 2;
	const static int kMaxMessageLen = 64 * 1024 * 1024;
};
}
}
#endif
